package com.stars.easyms.datasource.mybatis;

import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.mapper.ClassPathMapperScanner;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.util.Assert;

import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Set;

/**
 * <p>className: EasyMsClassPathMapperScanner</p>
 * <p>description: ClassPathMapperScanner自定义实现类</p>
 *
 * @author guoguifang
 * @version 1.2.1
 * @date 2019-05-08 10:27
 */
@Slf4j
final class EasyMsClassPathMapperScanner extends ClassPathMapperScanner {

    private ApplicationContext applicationContext;

    @Setter
    private List<Class<? extends Annotation>> annotationClassList;

    @Setter
    private List<Class<?>> markerInterfaceList;

    EasyMsClassPathMapperScanner(ApplicationContext applicationContext, BeanDefinitionRegistry registry) {
        super(registry);
        this.applicationContext = applicationContext;
    }

    @Override
    public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
        if (!beanDefinitions.isEmpty()) {
            beanDefinitions.forEach(beanDefinitionHolder -> {
                try {
                    applicationContext.getBean(beanDefinitionHolder.getBeanName());
                } catch (BeansException e) {
                    log.error("Unable to get bean '{}'!", beanDefinitionHolder.getBeanName());
                }

            });
        }
        return beanDefinitions;
    }

    @Override
    public void registerFilters() {
        boolean acceptAllInterfaces = true;

        if (!annotationClassList.isEmpty()) {
            annotationClassList.forEach(a -> addIncludeFilter(new AnnotationTypeFilter(a)));
            acceptAllInterfaces = false;
        }

        if (!markerInterfaceList.isEmpty()) {
            markerInterfaceList.forEach(m -> addIncludeFilter(new AssignableTypeFilter(m) {
                @Override
                protected boolean matchClassName(String className) {
                    return false;
                }
            }));
            acceptAllInterfaces = false;
        }

        if (acceptAllInterfaces) {
            addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
        }

        addExcludeFilter((metadataReader, metadataReaderFactory) -> {
            String className = metadataReader.getClassMetadata().getClassName();
            return className.endsWith("package-info");
        });
    }

    @Override
    protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) {
        BeanDefinitionRegistry registry = getRegistry();
        Assert.notNull(registry, "The BeanDefinitionRegistry instance is null!");
        if (!registry.containsBeanDefinition(beanName)) {
            return true;
        }
        BeanDefinition existingDef = registry.getBeanDefinition(beanName);
        BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();
        if (originatingDef != null) {
            existingDef = originatingDef;
        }
        if (isCompatible(beanDefinition, existingDef)) {
            return false;
        }
        throw new IllegalStateException("Annotation-specified bean name '" + beanName +
                "' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " +
                "non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]");
    }

}